package com.ikingtech.platform.business.message.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ikingtech.framework.sdk.base.model.BatchParam;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.message.MessageDeliverStatusEnum;
import com.ikingtech.framework.sdk.enums.message.MessageReadStatusEnum;
import com.ikingtech.framework.sdk.enums.message.MessageReceiverTypeEnum;
import com.ikingtech.framework.sdk.message.api.MessageManagementApi;
import com.ikingtech.framework.sdk.message.model.MessageDTO;
import com.ikingtech.framework.sdk.message.model.MessageQueryParamDTO;
import com.ikingtech.framework.sdk.message.model.rpc.MessageSendParam;
import com.ikingtech.framework.sdk.user.api.UserApi;
import com.ikingtech.framework.sdk.user.model.UserBasicDTO;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.business.message.entity.*;
import com.ikingtech.platform.business.message.exception.MessageExceptionInfo;
import com.ikingtech.platform.business.message.service.repository.*;
import com.ikingtech.platform.business.message.service.router.MessageRouteProxy;
import com.ikingtech.platform.business.message.service.router.RouteRequest;
import com.ikingtech.platform.business.message.service.router.RouteResult;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
@ApiController(value = "/message/management", name = "消息中心-消息管理", description = "消息中心-消息管理")
public class MessageManagementController implements MessageManagementApi {

    private final MessageRepository repo;

    private final MessageTemplateRepository templateRepo;

    private final MessageChannelDefinitionRepository channelDefinitionRepo;

    private final MessageReceiverDefinitionRepository receiverDefinitionRepo;

    private final MessageParamDefinitionRepository paramDefinitionRepo;

    private final MessageRedirectDefinitionRepository redirectDefinitionRepo;

    private final UserApi userApi;

    private final MessageRouteProxy routeProxy;

    /**
     * 分页查询消息列表
     * @param queryParam 查询参数
     * @return 返回消息列表
     */
    @Override
    public R<List<MessageDTO>> page(MessageQueryParamDTO queryParam) {
        // 调用服务层获取消息列表
        PageResult<MessageDO> entities = PageResult.build(this.repo.page(new Page<>(queryParam.getPage(), queryParam.getRows()), Wrappers.<MessageDO>lambdaQuery()
                .eq(Tools.Str.isNotBlank(queryParam.getBusinessKey()), MessageDO::getBusinessKey, queryParam.getBusinessKey())
                .in(Tools.Coll.isNotBlank(queryParam.getBusinessKeys()), MessageDO::getBusinessKey, queryParam.getBusinessKeys())
                .like(Tools.Str.isNotBlank(queryParam.getMessageContent()), MessageDO::getMessageContent, queryParam.getMessageContent())
                .eq(Tools.Str.isNotBlank(queryParam.getDeliverStatus()), MessageDO::getDeliverStatus, queryParam.getDeliverStatus())
                .eq(Tools.Str.isNotBlank(queryParam.getChannel()), MessageDO::getChannel, queryParam.getChannel())
                .eq(Tools.Str.isNotBlank(queryParam.getReadStatus()), MessageDO::getReadStatus, queryParam.getReadStatus())
                .eq(Tools.Str.isNotBlank(queryParam.getReceiverId()), MessageDO::getReceiverId, queryParam.getReceiverId())
                .eq(MessageDO::getTenantCode, Me.tenantCode())
                .orderByDesc(MessageDO::getCreateTime)));

        // 调用用户API获取用户基本信息
        List<UserBasicDTO> users = this.userApi.listInfoByIds(BatchParam.build(Tools.Coll.convertList(entities.getData(), MessageDO::getReceiverId))).getData();

        // 将用户信息转换为Map
        Map<String, UserBasicDTO> userMap = Tools.Coll.convertMap(users, UserBasicDTO::getId);

        // 将消息列表转换为MessageDTO列表
        return R.ok(entities.getPages(), entities.getTotal(), Tools.Coll.convertList(entities.getData(), entity -> {
            MessageDTO message = Tools.Bean.copy(entity, MessageDTO.class);

            // 设置渠道名称
            if (null != message.getChannel()) {
                message.setChannelName(message.getChannel().description);
            }

            // 设置送达状态名称
            if (null != message.getDeliverStatus()) {
                message.setDeliverStatusName(message.getDeliverStatus().description);
            }

            // 设置已读状态名称
            if (null != message.getReadStatus()) {
                message.setReadStatusName(message.getReadStatus().description);
            }

            // 设置送达状态名称
            if (null != message.getDeliverStatus()) {
                message.setDeliverStatusName(message.getDeliverStatus().description);
            }

            // 设置接收者名称
            if (userMap.containsKey(entity.getReceiverId())) {
                message.setReceiverName(userMap.get(entity.getReceiverId()).getName());
            }

            return message;
        }));
    }

    /**
     * 根据id读取消息
     * @param id 消息id
     * @return 返回读取消息的结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> readById(String id) {
        // 获取消息实体
        MessageDO entity = this.repo.getById(id);
        // 如果消息实体为空，则抛出异常
        if (null == entity) {
            throw new FrameworkException(MessageExceptionInfo.MESSAGE_NOT_FOUND);
        }
        // 设置消息已读状态和时间
        entity.setReadStatus(MessageReadStatusEnum.READ.name());
        entity.setReadTime(LocalDateTime.now());
        // 更新消息实体
        this.repo.updateById(entity);
        // 返回读取消息的结果
        return R.ok();
    }

    /**
     * 读取消息列表
     * @return 返回操作结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> readAll() {
        // 获取未读消息列表
        List<MessageDO> entities = this.repo.list(Wrappers.<MessageDO>lambdaQuery().eq(MessageDO::getReadStatus, MessageReadStatusEnum.NO_READ).eq(MessageDO::getReceiverId, Me.id()));
        // 更新消息列表的读状态和读取时间
        this.repo.updateBatchById(Tools.Coll.traverse(entities, entity -> {
            entity.setReadStatus(MessageReadStatusEnum.READ.name());
            entity.setReadTime(LocalDateTime.now());
            return entity;
        }));
        // 返回操作结果
        return R.ok();
    }

    /**
     * 发送消息
     *
     * @param sendParam 消息发送参数
     * @return 操作结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> send(MessageSendParam sendParam) {
        //通过消息标识，获取消息模板
        MessageTemplateDO templateEntity = this.templateRepo.getOne(Wrappers.<MessageTemplateDO>lambdaQuery().eq(MessageTemplateDO::getMessageTemplateKey, sendParam.getMessageTemplateKey()));
        if (null == templateEntity) {
            throw new FrameworkException(MessageExceptionInfo.MESSAGE_TEMPLATE_NOT_FOUND);
        }

        List<MessageDO> entities = new ArrayList<>();

        //通过模板id获取消息通道定义,通道指的是系统消息,钉钉消息,短信等内部或外部系统的通道
        List<MessageChannelDefinitionDO> channelDefinitionEntities = this.channelDefinitionRepo.list(Wrappers.<MessageChannelDefinitionDO>lambdaQuery()
                .eq(MessageChannelDefinitionDO::getTemplateId, templateEntity.getId()));

        //消息接收者
        List<MessageReceiverDefinitionDO> receiverDefinitionEntities = this.receiverDefinitionRepo.list(Wrappers.<MessageReceiverDefinitionDO>lambdaQuery()
                .eq(MessageReceiverDefinitionDO::getTemplateId, templateEntity.getId()));
        //获取到所有的接收人信息
        List<UserBasicDTO> users = this.parseReceiverUser(receiverDefinitionEntities, sendParam.getMessage());

        //获取所有的消息定义,并根据通道分组
        List<MessageParamDefinitionDO> paramDefinitionEntities = this.paramDefinitionRepo.list(Wrappers.<MessageParamDefinitionDO>lambdaQuery()
                .eq(MessageParamDefinitionDO::getTemplateId, templateEntity.getId())
                .ne(MessageParamDefinitionDO::getPreDefinition, true)
                .orderByAsc(MessageParamDefinitionDO::getSortOrder));
        Map<String, List<MessageParamDefinitionDO>> paramDefinitionMap = Tools.Coll.convertGroup(paramDefinitionEntities, MessageParamDefinitionDO::getChannelDefinitionId);

        //获取所有的消息重定向定义,并根据通道分组
        List<MessageRedirectDefinitionDO> redirectDefinitionEntities = this.redirectDefinitionRepo.list(Wrappers.<MessageRedirectDefinitionDO>lambdaQuery()
                .eq(MessageRedirectDefinitionDO::getTemplateId, templateEntity.getId()));
        Map<String, List<MessageRedirectDefinitionDO>> redirectDefinitionMap = Tools.Coll.convertGroup(redirectDefinitionEntities, MessageRedirectDefinitionDO::getChannelDefinitionId);

        channelDefinitionEntities.forEach(channelDefinitionEntity ->
                users.forEach(user -> {
                    //根据通道获取消息路由代理,并执行route方法构造及发送消息
                    RouteResult result = this.routeProxy.determine(channelDefinitionEntity.getChannel())
                            .route(RouteRequest.builder()
                                    .templateId(templateEntity.getId())
                                    .channelDefinition(channelDefinitionEntity)
                                    .paramMap(Tools.Coll.convertMap(paramDefinitionMap.get(channelDefinitionEntity.getId()), MessageParamDefinitionDO::getParamName, paramDefinitionEntity -> Tools.Str.isBlank(paramDefinitionEntity.getMappedParamName()) ? Tools.Str.EMPTY : paramDefinitionEntity.getMappedParamName()))
                                    .redirect(Tools.Coll.convertMap(redirectDefinitionMap.get(channelDefinitionEntity.getId()), MessageRedirectDefinitionDO::getRedirectName, MessageRedirectDefinitionDO::getRedirectTo))
                                    .target(user)
                                    .messageWrapper(sendParam.getMessage())
                                    .build());
                    MessageDO entity = new MessageDO();
                    entity.setId(Tools.Id.uuid());
                    entity.setMessageContent(result.getContent());
                    entity.setRedirectTo(Tools.Coll.join(result.getRedirectTo()));
                    entity.setCause(result.getCause());
                    entity.setDeliverStatus((result.getSuccess() != null && result.getSuccess()) ? MessageDeliverStatusEnum.DELIVERED.name() : MessageDeliverStatusEnum.DELIVER_FAILED.name());
                    entity.setReadStatus(MessageReadStatusEnum.NO_READ.name());
                    entity.setTemplateId(templateEntity.getId());
                    entity.setBusinessName(result.getBusinessName());
                    entity.setBusinessKey(templateEntity.getBusinessKey());
                    entity.setMessageTemplateKey(templateEntity.getMessageTemplateKey());
                    entity.setMessageTitle(result.getMessageTitle());
                    entity.setChannel(channelDefinitionEntity.getChannel());
                    entity.setDeliverTime(LocalDateTime.now());
                    entity.setReceiverId(user.getId());
                    entity.setTenantCode(Tools.Str.isBlank(sendParam.getTenantCode()) ? Me.tenantCode() : sendParam.getTenantCode());
                    entities.add(entity);
                }));
        //记录发送的消息
        if (Tools.Coll.isNotBlank(entities)) {
            this.repo.saveBatch(entities);
        }
        // 返回操作结果
        return R.ok();
    }

    private List<UserBasicDTO> parseReceiverUser(List<MessageReceiverDefinitionDO> receiverDefinitionEntities, Object messageWrapper) {
        Map<String, Object> messageParamMap = Tools.Json.objToMap(messageWrapper);
        Map<String, List<String>> receiverTypeMap = new HashMap<>();

        receiverDefinitionEntities.forEach(entity -> {
            //从消息体中根据接收方参数名称获取接收人员ids
            List<String> receiverIds = this.parseReceiverIdFromParam(messageParamMap, entity.getReceiverParamName());
            if (Tools.Coll.isNotBlank(receiverIds)) {
                //转为map,map的key是接收者类型,值是接收人员ids
                receiverTypeMap.computeIfAbsent(entity.getReceiverType(), k -> new ArrayList<>()).addAll(receiverIds);
            }
        });

        List<UserBasicDTO> users = new ArrayList<>();
        receiverTypeMap.forEach((receiverType, receiverIds) -> {
            //根据接收者类型和接收人员ids获取用户信息
            if (MessageReceiverTypeEnum.USER.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByIds(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.MENU.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByMenuCodes(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.ROLE.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByRoleIds(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.POST.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByPostIds(BatchParam.build(receiverIds)).getData());
            }
            if (MessageReceiverTypeEnum.POST.name().equals(receiverType)) {
                users.addAll(this.userApi.listInfoByPostIds(BatchParam.build(receiverIds)).getData());
            }
        });
        return users;
    }

    private List<String> parseReceiverIdFromParam(Map<String, Object> messageParamMap, String paramName) {
        Object paramValue = messageParamMap.get(paramName);
        if (null == paramValue) {
            return new ArrayList<>();
        }
        if (paramValue instanceof List) {
            return Tools.Json.objToBean(messageParamMap.get(paramName), new TypeReference<>() {
            });
        }
        if (paramValue instanceof String) {
            return Collections.singletonList((String) paramValue);
        }
        return new ArrayList<>();
    }
}
